package com.zztl.infiniteBand.util

import android.annotation.TargetApi
import android.content.Context
import android.icu.text.AlphabeticIndex
import android.os.Build
import android.util.Log
import java.lang.reflect.Method
import java.util.*

class AlphabeticIndexCustomCompat(context: Context) {
    private val mBaseIndex: BaseIndex
    private val mDefaultMiscLabel: String

    init {
        var index: BaseIndex? = null

        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                index = AlphabeticIndexVN(context)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Log.d(TAG, "Unable to load the system index")
        }

        if (index == null) {
            try {
                index = AlphabeticIndexV16(context)
            } catch (e: Exception) {
                e.printStackTrace()
                Log.d(TAG, "Unable to load the system index")
            }

        }

        mBaseIndex = if (index == null) BaseIndex() else index

        if (context.resources.configuration.locale
                        .language == Locale.JAPANESE.language) {
            // Japanese character 他 ("misc")
            mDefaultMiscLabel = "\u4ed6"
            // TODO(winsonc, omakoto): We need to handle Japanese sections better, especially the kanji
        } else {
            // Dot
            mDefaultMiscLabel = MID_DOT
        }
    }

    /**
     * Computes the section name for an given string {@param s}.
     */
    fun computeSectionName(cs: CharSequence): String {
        val s = cs.toString().trim { it <= ' ' }
        val sectionName = mBaseIndex.getBucketLabel(mBaseIndex.getBucketIndex(s))
        if (sectionName.trim { it <= ' ' }.isEmpty() && s.length > 0) {
            val c = s.codePointAt(0)
            val startsWithDigit = Character.isDigit(c)
            if (startsWithDigit) {
                // Digit section
                //
                //                return "#";
                return MID_DOT
            } else {
                val startsWithLetter = Character.isLetter(c)
                return if (startsWithLetter) {
                    mDefaultMiscLabel
                } else {
                    // In languages where these differ, this ensures that we differentiate
                    // between the misc section in the native language and a misc section
                    // for everything else.
                    MID_DOT
                }
            }
        }
        return sectionName
    }

    fun computeSectionIndex(cs: CharSequence): Int {
        val str = computeSectionName(cs)
        return mBaseIndex.getBucketIndex(str)
    }

    fun getBucketIndex(str: String): Int {
        return mBaseIndex.getBucketIndex(str)
    }

    /**
     * Base class to support Alphabetic indexing if not supported by the framework.
     * TODO(winsonc): disable for non-english locales
     */
    private open class BaseIndex {

        /**
         * Returns the index of the bucket in which the given string should appear.
         */
        open fun getBucketIndex(s: String): Int {
            if (s.isEmpty()) {
                return UNKNOWN_BUCKET_INDEX
            }
            val index = BUCKETS.indexOf(s.substring(0, 1).toUpperCase())
            return if (index != -1) {
                index
            } else UNKNOWN_BUCKET_INDEX
        }

        /**
         * Returns the label for the bucket at the given index (as returned by getBucketIndex).
         */
        open fun getBucketLabel(index: Int): String {
            return BUCKETS.substring(index, index + 1)
        }

        companion object {

            private val BUCKETS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-"
            private val UNKNOWN_BUCKET_INDEX = BUCKETS.length - 1
        }
    }

    /**
     * Reflected libcore.icu.AlphabeticIndex implementation, falls back to the base
     * alphabetic index.
     */
    private class AlphabeticIndexV16 @Throws(Exception::class)
    constructor(context: Context) : BaseIndex() {

        private val mAlphabeticIndex: Any
        private val mGetBucketIndexMethod: Method
        private val mGetBucketLabelMethod: Method

        init {
            val curLocale = context.resources.configuration.locale
            val clazz = Class.forName("libcore.icu.AlphabeticIndex")
            mGetBucketIndexMethod = clazz.getDeclaredMethod("getBucketIndex", String::class.java)
            mGetBucketLabelMethod = clazz.getDeclaredMethod("getBucketLabel", Int::class.javaPrimitiveType)
            mAlphabeticIndex = clazz.getConstructor(Locale::class.java).newInstance(curLocale)

            if (curLocale.language != Locale.ENGLISH.language) {
                clazz.getDeclaredMethod("addLabels", Locale::class.java)
                        .invoke(mAlphabeticIndex, Locale.ENGLISH)
            }
        }

        /**
         * Returns the index of the bucket in which {@param s} should appear.
         * Function is synchronized because underlying routine walks an iterator
         * whose state is maintained inside the index object.
         */
        override fun getBucketIndex(s: String): Int {
            try {
                return mGetBucketIndexMethod.invoke(mAlphabeticIndex, s) as Int
            } catch (e: Exception) {
                e.printStackTrace()
            }

            return super.getBucketIndex(s)
        }

        /**
         * Returns the label for the bucket at the given index (as returned by getBucketIndex).
         */
        override fun getBucketLabel(index: Int): String {
            try {
                return mGetBucketLabelMethod.invoke(mAlphabeticIndex, index) as String
            } catch (e: Exception) {
                e.printStackTrace()
            }

            return super.getBucketLabel(index)
        }
    }

    /**
     * Implementation based on [AlphabeticIndex].
     */
    @TargetApi(Build.VERSION_CODES.N)
    private class AlphabeticIndexVN(context: Context) : BaseIndex() {

        private val mAlphabeticIndex: AlphabeticIndex.ImmutableIndex<*>

        init {
            val locales = context.resources.configuration.locales
            val localeCount = locales.size()

            val primaryLocale = if (localeCount == 0) Locale.ENGLISH else locales.get(0)
            val indexBuilder = AlphabeticIndex<Any>(primaryLocale)
            for (i in 1 until localeCount) {
                indexBuilder.addLabels(locales.get(i))
            }
            indexBuilder.addLabels(Locale.ENGLISH)

            mAlphabeticIndex = indexBuilder.buildImmutableIndex()
        }

        /**
         * Returns the index of the bucket in which {@param s} should appear.
         */
        override fun getBucketIndex(s: String): Int {
            return mAlphabeticIndex.getBucketIndex(s)
        }

        /**
         * Returns the label for the bucket at the given index
         */
        override fun getBucketLabel(index: Int): String {
            return mAlphabeticIndex.getBucket(index).label
        }
    }

    companion object {
        private val TAG = "IndexCostomCompat"

        private val MID_DOT = "\u2219"
        private val OTHER = "#"
    }
}
